import calendar
import datetime
import dateutil.parser
import typing

# Many many years later....
PERPETUAL_DATE = datetime.datetime(3000, 1, 1)
# Many many years before....
IMMEDIATE_DATE = datetime.datetime(2000, 1, 1)


def to_expiry_datetime(expiry: typing.Union[datetime.datetime, datetime.date, str]):
  if isinstance(expiry, str):
    if expiry == 'PERPETUAL':
      res = PERPETUAL_DATE
    elif expiry == 'IMMEDIATE':
      res = IMMEDIATE_DATE
    else:
      res = dateutil.parser.parse(expiry)
  elif isinstance(expiry, datetime.date):
    res = datetime.datetime.combine(expiry, datetime.time(0))
  elif isinstance(expiry, datetime.datetime):
    res = expiry
  else:
    raise TypeError('Unsupported input type! %s' % type(expiry))

  assert isinstance(res, datetime.datetime)

  if res > PERPETUAL_DATE:
    res = PERPETUAL_DATE
  elif res < IMMEDIATE_DATE:
    res = IMMEDIATE_DATE
  return res


def to_expiry_str(expiry, fmt=None):
  if expiry == PERPETUAL_DATE:
    expiry_str = 'PERPETUAL'
  elif expiry == IMMEDIATE_DATE:
    expiry_str = 'IMMEDIATE'
  else:
    if fmt is not None:
      expiry_str = expiry.strftime(fmt)
    else:
      expiry_str = expiry.strftime('%Y%m%d')
  return expiry_str


def assert_expiry_str(expiry):
  special_expiry = ('PERPETUAL',
                    'IMMEDIATE',
                    'WEEK',
                    'THIS_WEEK',
                    'NEXT_WEEK',
                    'QUARTER',
                    'CURRENT_WEEK',
                    'CURRENT_QUARTER',
                    'MONTH',
                    'NEXT_QUARTER')
  if expiry not in special_expiry:
    assert len(expiry) == 8 and expiry.isdigit() and expiry.startswith('20'), ("Invalid Expiry",
                                                                               expiry)


def get_last_friday(year, month):
  cal = calendar.Calendar(4)  # Make Friday the first day.
  week_list = cal.monthdayscalendar(year, month)
  fridays = [week[0] for week in week_list if week[0] != 0]
  return datetime.datetime(year, month, fridays[-1])


def get_last_month_of_quarter(current_datetime):
  month = current_datetime.month
  last_month = (((month - 1) // 3) + 1) * 3
  return last_month
